package com.timsmeet.timeperiod;

import java.util.Calendar;

import com.timsmeet.utils.ArgumentValidator;

public class TimeBlockImpl implements TimeBlock {
	
	private Calendar start;
	private TimeInterval duration;
	private Calendar end; // cache

	public TimeBlockImpl(Calendar start, Calendar end) {
		ArgumentValidator.isNotNull(start, "start");
		ArgumentValidator.isNotNull(end, "end");

		if(start.compareTo(end) <= 0) {
			this.start = TimePeriodUtils.trimCalendar(start);
			this.end = TimePeriodUtils.trimCalendar(end);
		} else {
			this.end = TimePeriodUtils.trimCalendar(start);
			this.start = TimePeriodUtils.trimCalendar(end);
		}
		this.duration = new TimeInterval((end.getTimeInMillis() - start.getTimeInMillis()) / (TimePeriod.MILISECONDS_IN_MINUTE));
	}

	public TimeBlockImpl(Calendar start, TimeInterval duration) {
		ArgumentValidator.isNotNull(start, "start");
		ArgumentValidator.isNotNull(duration, "duration");
		
		this.start = TimePeriodUtils.trimCalendar(start);
		this.end = TimePeriodUtils.trimCalendar(start);
		this.duration = duration;
		if(this.duration.getMinutes() > Integer.MAX_VALUE) {
			throw new ArithmeticException("Interval to large to create TimeRange");
		}
		this.end.add(Calendar.MINUTE, (int)this.duration.getMinutes());
	}
	
	public TimeBlockImpl(TimeInterval duration, Calendar end) {
		ArgumentValidator.isNotNull(end, "end");
		ArgumentValidator.isNotNull(duration, "duration");
		
		this.end = TimePeriodUtils.trimCalendar(end);
		this.start = TimePeriodUtils.trimCalendar(end);
		this.duration = duration;
		if(this.duration.getMinutes() > Integer.MAX_VALUE) {
			throw new ArithmeticException("Interval to large to create TimeRange");
		}
		this.start.add(Calendar.MINUTE, - (int)this.duration.getMinutes());
	}

	
	
	@Override
	public Calendar getStart() {
		return start;
	}

	@Override
	public void setStart(Calendar start) {
		ArgumentValidator.isNotNull(start, "start");
		this.start = TimePeriodUtils.trimCalendar(start);
		this.end = TimePeriodUtils.trimCalendar(start);
		this.end.add(Calendar.MINUTE, (int)this.duration.getMinutes());
	}

	@Override
	public Calendar getEnd() {
		return end;
	}

	@Override
	public void setEnd(Calendar end) {
		ArgumentValidator.isNotNull(end, "end");
		this.end = TimePeriodUtils.trimCalendar(end);
		this.start = TimePeriodUtils.trimCalendar(end);
		this.start.add(Calendar.MINUTE, - (int)this.duration.getMinutes());
	}

	@Override
	public boolean isMoment() {
		if(start != null && end != null) {
			return (TimePeriodUtils.compareTrimedCalendar(start, end) == 0);
		}
		return false;
	}

	@Override
	public TimeInterval getDuration() {
		return duration;
	}

	@Override
	public PeriodRelation getRelation(TimePeriod period) {
		ArgumentValidator.isNotNull(period, "period");
		return TimePeriodCalc.getRelation(this, period);
	}

	@Override
	public boolean hasInside(TimePeriod period) {
		ArgumentValidator.isNotNull(period, "period");
		return TimePeriodCalc.hasInside(this, period);
	}

	@Override
	public boolean intersectsWith(TimePeriod period) {
		ArgumentValidator.isNotNull(period, "period");
		return TimePeriodCalc.intersectsWith(this, period);
	}

	@Override
	public boolean overlapsWith(TimePeriod period) {
		ArgumentValidator.isNotNull(period, "period");
		return TimePeriodCalc.overlapsWith(this, period);
	}

	@Override
	public boolean isSamePeriod(TimePeriod period) {
		ArgumentValidator.isNotNull(period, "period");
		return TimePeriodCalc.getRelation(this, period).equals(PeriodRelation.EXACT_MATCH);
	}


	@Override
	public void durationFromStart(TimeInterval newDuration) {
		ArgumentValidator.isNotNull(newDuration, "newDuration");
		this.duration = newDuration;
		this.end = TimePeriodUtils.trimCalendar(start);
		this.end.add(Calendar.MINUTE, (int)duration.getMinutes());
	}

	@Override
	public void durationFromEnd(TimeInterval newDuration) {
		ArgumentValidator.isNotNull(newDuration, "newDuration");
		this.duration = newDuration;
		this.start = TimePeriodUtils.trimCalendar(end);
		this.start.add(Calendar.MINUTE, - (int)duration.getMinutes());
	}

	@Override
	public TimeBlock getIntersection(TimePeriod period) {
		ArgumentValidator.isNotNull(period, "period");
		
		if(!intersectsWith(period)) {
			return null;
		}
		Calendar resultStart = period.getStart().after(start) ? period.getStart() : start;
		Calendar resultEnd = period.getEnd().before(end) ? period.getEnd() : end;
		return new TimeBlockImpl(resultStart, resultEnd);
	}

	@Override
	public String toString() {
		return "TimeBlockImpl [start=[" + getFormatCalendar(start) + "], end=[" + getFormatCalendar(end) + "], duration=[" + getDuration().getMinutes() + " min]]";
	}
	
	
	private String getFormatCalendar(Calendar calendar) {
		return
			"Year: " + calendar.get(Calendar.YEAR) +
			", Month: " + calendar.get(Calendar.MONTH) +
			", Day: " + calendar.get(Calendar.DAY_OF_MONTH) +
			", Hour: " + calendar.get(Calendar.HOUR_OF_DAY) + 
			", Minutes: " + calendar.get(Calendar.MINUTE);
	}
	
}
